package scheduled

import (
	"fmt"
	"log"
	"strconv"
	"time"

	"gitee.com/liuyun-luo/MagicStreamMovies/Server/config"
	"gitee.com/liuyun-luo/MagicStreamMovies/Server/global"
	"gitee.com/liuyun-luo/MagicStreamMovies/Server/models"
	"github.com/go-co-op/gocron"
	"github.com/go-redis/redis"
	"gorm.io/gorm"
)

// 定时任务：定期将redis中的点赞写入到数据库
func SyncSaveMovieLikeCount() error {
	log.Println("开始同步缓存中的电影的点赞到MySQL...")

	//检查电影点赞脏数据
	movie_id_list, err := global.RedisDB.SMembers(config.MoviesLikeDirtySetKeyName).Result()
	if err != nil {
		log.Printf("电影的点赞同步到MySQL失败,err: %v", err.Error())
		return err
	}
	if len(movie_id_list) == 0 {
		log.Println("没有需要同步的点赞")
		return nil
	}

	type movieInfo struct {
		ID        uint
		LikeCount uint64
	}

	cmds := make(map[int]*redis.StringCmd)

	//扩容
	movie_info_list := make([]movieInfo, len(movie_id_list))

	//启动Pipeline获取所有被点赞的电影当前的点赞数
	pipe := global.RedisDB.Pipeline()
	{
		for index, movie_id := range movie_id_list {
			id, _ := strconv.ParseUint(movie_id, 10, 64)

			movie_info_list[index].ID = uint(id) //填充id

			//拼接点赞键名
			key := config.MovieKeyName + ":" + movie_id + ":" + config.MovieLikeCountKeyName

			//记录当前下标对应的结果
			cmds[index] = pipe.Get(key)
		}
	}
	//提交Pipeline
	if _, err := pipe.Exec(); err != nil {
		log.Printf("电影的点赞同步到MySQL失败,err: %v", err.Error())
		return err
	}

	//遍历结果map
	for index, cmd := range cmds {
		countStr, err := cmd.Result()
		if err == redis.Nil {
			// 键不存在，可能已被删除，跳过
			//为零值时不会更新
			movie_info_list[index].ID = 0
			movie_info_list[index].LikeCount = 0

			continue
		} else if err != nil {
			log.Printf("获取电影id为: %d的点赞键的值时出错: %v", movie_info_list[index].ID, err)
			//为零值时不会更新
			movie_info_list[index].ID = 0
			movie_info_list[index].LikeCount = 0
			continue
		}
		like, _ := strconv.ParseUint(countStr, 10, 64)

		movie_info_list[index].LikeCount = like //填充点赞
	}

	// 同步成功后，清空脏数据集合
	// 更安全的做法是，只删除我们成功处理了的key，这里简化示例
	_, err = global.RedisDB.Del(config.MoviesLikeDirtySetKeyName).Result()
	if err == redis.Nil || err == nil { //找不到也就是被别人删了
		log.Println("脏数据集合已清空")
	} else {
		log.Printf("清空脏数据集合失败: %v", err)
		// 可以选择不return，因为主数据已同步，下次同步会覆盖
	}

	log.Println(movie_info_list) //=========

	var caseLikeCount string // 用于更新 like_count 的 CASE 语句
	var ids []uint           // 用于限定更新范围的 ID 列表

	//构建UpedateSQL
	for _, info := range movie_info_list {
		if info.ID == 0 { //表示这个键异常，跳过即可,无需更新
			continue
		}
		caseLikeCount += fmt.Sprintf("WHEN id = %d THEN %d ", info.ID, info.LikeCount)
		ids = append(ids, info.ID) //要更新的电影的id
	}
	// 拼接完整的 CASE 语句（注意：GORM v1 会自动处理字段名，无需显式加表名）
	caseLikeCount = "CASE " + caseLikeCount + "END"

	if len(ids) != 0 { //要更新的电影列表不为空，才需要更新
		//批量写入MySQL
		// 关键点：用 Where 限定 ID 范围，避免全表更新；用 gorm.Expr 传递 CASE 语句
		if err := global.MySQLDb.Debug().Model(&models.Movie{}).
			Where("id IN (?)", ids).                                           // 只更新指定 ID 的记录
			Update("like_count", gorm.Expr(caseLikeCount)).Error; err != nil { // 更新 like_count 字段
			log.Printf("批量写入MySQL失败,err: %v", err.Error())
			return err
		}
	}

	log.Println("电影的点赞同步MySQL成功！")
	return nil
}

// 启动定时任务
func StartSyncSaveMovieLikeCount() {
	ticker := time.NewTicker(30 * time.Second)

	go func() {
		for range ticker.C {
			if err := SyncSaveMovieLikeCount(); err != nil {
				log.Println("执行SyncSaveMovieLikeCount()任务失败,err: ", err.Error())
			}
		}
	}()
}

// 启动所有定时任务
func StartSyncTimeTask() {
	// 定时任务：定期将redis中的点赞写入到数据库
	StartSyncSaveMovieLikeCount()
}

// 更新每周一的基准时间
func updateMondayBaseTime() {
	newBaseTime := time.Now()

	global.MondayBaseTimeMutex.Lock()
	global.MondayBaseTime = newBaseTime // 更新基准时间
	global.MondayBaseTimeMutex.Unlock()

	log.Printf("基准时间已更新为: %s\n", newBaseTime.Format("2006-01-02 15:04:05"))
	// 这里可以添加更多逻辑，如持久化到数据库、文件等
}

// 初始化并启动Gocron调度器
func InitGocron() {
	// 创建一个调度器，并设置使用本地时区
	s := gocron.NewScheduler(time.Local)

	// 安排任务：每周一的凌晨1点执行updateBaseTime函数
	// 注意：gocron的Week()默认从周日(0)开始，周一为1
	_, err := s.Every(1).Week().Monday().At("01:00").Do(updateMondayBaseTime)
	if err != nil {
		log.Fatalf("安排任务失败: %v", err)
	}

	// 启动调度器（异步执行）
	s.StartAsync()

	log.Println("每周基准时间更新任务已启动...")
}
